package com.mischa.atc.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import com.mischa.atc.Coordinate;
import com.mischa.atc.Flight;
import com.mischa.atc.RadarScreenDrawer;
import com.mischa.atc.Utilities;

public class RadarScreenCanvas 
	extends DoubleBufferedCanvas
	implements RadarScreenDrawer
	{

	private static final long serialVersionUID = 1L;
		
	private Dictionary<Flight, List<Coordinate>> _flightsToDraw;
	private int _sweepHeading;
	private int _canvasSize;
	private int _radius;
	final int NUM_OF_POSITIONS_IN_HISTORY = 5;		
	
	public RadarScreenCanvas() {	
		_flightsToDraw = new Hashtable<Flight, List<Coordinate>>();
	}
	
	public void DrawFlight(Flight flight, float x, float y) {
		List<Coordinate> positions = _flightsToDraw.get(flight);
		if(positions == null)
		{
			positions = new ArrayList<Coordinate>();
			_flightsToDraw.put(flight, positions);
		}
		positions.add(0, new Coordinate(x, y));
		while(positions.size() > NUM_OF_POSITIONS_IN_HISTORY)
		{
			positions.remove(NUM_OF_POSITIONS_IN_HISTORY);
		}
		repaint();
	}
	
	int _lineHeight;
	private int getLineHeight(Graphics g)
	{
		if(_lineHeight == 0)
		{
			FontMetrics metrics = g.getFontMetrics();
			_lineHeight = metrics.getHeight();
		}
		return _lineHeight;
	}

	
	public void paint(Graphics g) {
		int markerSize = 6;
		_canvasSize = Math.min(getWidth(), getHeight());
		
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, getWidth(), getHeight());		
		
		Enumeration<Flight> flights = _flightsToDraw.keys();
		while(flights.hasMoreElements())
		{
			Flight flight = flights.nextElement();
			List<Coordinate> positionHistory = _flightsToDraw.get(flight);
			
			g.setColor(Color.GREEN);
			for(int i = 0; i < positionHistory.size(); ++i)
			{				
				drawPosition(positionHistory.get(i), g);
				g.setColor(g.getColor().darker());
			}
			
			g.setColor(Color.GREEN);
			Point labelPosition = relativeCoordinatesToCanvasCoordinates(positionHistory.get(0));
						
			g.drawString(flight.toString(), labelPosition.x + markerSize, labelPosition.y);
			g.drawString(altitudeLabel(flight), labelPosition.x + markerSize, labelPosition.y + getLineHeight(g));
			g.drawString(flight.getHeading().toString() + " " + Utilities.padInteger(flight.getSpeed(), 3), labelPosition.x + markerSize, labelPosition.y + 2 * getLineHeight(g));
		}
		
		g.setColor(Color.GRAY);
		int centerX = getWidth() / 2;
		int centerY = getHeight() / 2;
		// Note: we get a minus sign, because "up" is "more negative y"
		Point endPoint = relativeCoordinatesToCanvasCoordinates(
				new Coordinate(
						(float) Math.sin(Math.toRadians(_sweepHeading)), 
						(float) Math.cos(Math.toRadians(_sweepHeading))
				));
		g.drawLine(centerX, centerY,
				endPoint.x, endPoint.y);
				// centerX + (int) (_canvasSize / 2f * Math.sin(Math.toRadians(_sweepHeading))),
				// centerY - (int) (_canvasSize / 2f * Math.cos(Math.toRadians(_sweepHeading))));
		g.drawString(String.format("Radius: %d nm", _radius), 0, getLineHeight(g));
	}
	
	private String altitudeLabel(Flight flight) {
		String result = Utilities.padInteger(flight.getAltitude() / 100, 3);
		if(flight.getAltitude() == flight.getClearedAltitude())
			result += "=";
		else if(flight.getAltitude() < flight.getClearedAltitude())
			result += "^" + Utilities.padInteger(flight.getClearedAltitude() / 100, 3);
		else
			result += "v" + Utilities.padInteger(flight.getClearedAltitude() / 100, 3);
		
		return result;
	}

	static final int positionMarkerSize = 6;
	
	private void drawPosition(Coordinate position, Graphics g)
	{		
		Point canvasCoordinates = relativeCoordinatesToCanvasCoordinates(position);		
		g.drawRect(canvasCoordinates.x, canvasCoordinates.y, positionMarkerSize / 2, positionMarkerSize / 2);
	}
	
	/**
	 * Converts original relative coordinates (i.e. in range -1 to 1) 
	 * to actual coordinates on the canvas, where (0, 0) corresponds to the center
	 * of the canvas.
	 */
	private Point relativeCoordinatesToCanvasCoordinates(Coordinate original)
	{
		int x = (int) (getWidth() / 2 + original.getX() * _canvasSize / 2);
		int y = (int) (getHeight() / 2 - original.getY() * _canvasSize / 2);
		
		return new Point(x, y);
	}

	@Override
	public void sweeping(int heading) {
		_sweepHeading = heading;
		repaint();
	}

	@Override
	public void flightOutOfRange(Flight flight) {
		_flightsToDraw.remove(flight);
		repaint();
	}

	public void setRadius(int radius) {
		_radius = radius;
	}

}
